iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 20
0

文章本身不會特別解釋gRPC 與protocol buffers,如果有興趣可以去看官網文件會更精準一點 grpc.io,主體還是放在如何實作gRPC server/client與定義protobuf

簡單說一下為什麼要用gRPC
1.HTTP/2 進行通訊協定,效能好
2.Protobuf為二進位而且檔案小
3.如果傳輸大量資料時,gRPC比較不會炸掉
4.相對安全,因為RESTFUL API只要猜到METHOD,HEADER就有機會try到,可是用gRPC的話,Client也要拿的到protobuf才有辦法傳遞,如果protobuf放在內部repo,要被外部try到機率已經低很多惹!!

環境安裝

1.Protocol Compiler Installation:官方安裝文件
2.Go protocol buffers plugin:go get github.com/golang/protobuf/protoc-gen-go
3.Golang grpc package:go get -u google.golang.org/grpc

安裝後可以試看看是否環境都設定好

$protoc --version
//libprotoc 3.12.3

定義proto檔

目前使用的版本為v3,所以syntax要指定proto3

syntax = "proto3";

package mdfk2020;

//定義Service名稱,
service SaySomethingService{ 
  //定義api名稱,傳入參數與回傳值
  rpc MDFK2020 (MDFKRequest) returns (MDFKResponse) {}
}

//傳入參數的spec
message MDFKRequest {
  string name = 1;  
}

//回傳值的spec
message MDFKResponse {
  string data = 1;
}

定義好之後執行

$protoc --go_out=plugins=grpc:. *.proto

目前版本會跳出一個提示訊息

WARNING: Missing 'go_package' option in "MDFK2020.proto", please specify:
        option go_package = ".;mdfk2020";
A future release of protoc-gen-go will require this be specified.
See https://developers.google.com/protocol-buffers/docs/reference/go-generated#package for more information.

但是還是會產生檔案,如果不要跳出提示訊息的話,需要在proto裡面加入option go_package = ".;mdfk2020";

syntax = "proto3";
option go_package = ".;mdfk2020";
package mdfk2020;

會產生一個.pb.go的檔案,打開看會發現protoc cli會把proto定義好的service、func、resquest與response產生成go的檔案

實作server與client端

如果pb檔不是放在github上面,而是放在local要import進來用的話,要記得import的路徑,
import的路徑是以設定好的gopath進來判斷,以下圖為例子

//完整路徑~/GoProjects/src/internal/grpcdemo/pb
import pb "internal/grpcdemo/pb"

server

1.實作proto定義好的service
2.運行gRPC服務

package main

import (
	"context"
	"fmt"
	"log"
	"net"

	pb "internal/grpcdemo/pb"

	"google.golang.org/grpc"
)

const (
	port = ":8787"
)

type server struct{}

func main() {
	// Create gRPC Server
	lis, err := net.Listen("tcp", port)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	log.Println("gRPC server is running.")

	pb.RegisterSaySomethingServiceServer(s, &server{})
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

//
func (s *server) MDFK2020(ctx context.Context, in *pb.MDFKRequest) (*pb.MDFKResponse, error) {
	fmt.Println("i receive:", in.Name)
	return &pb.MDFKResponse{Data: "Hello " + in.Name}, nil
}


client

client的使用流程相對單純,使用grpc.Dial()連接gRPC Server,在用回傳的conn去呼叫定義好的service ,參數與回傳值都跟proto定義好的一致就行。

package main

import (
	"context"
	pb "internal/grpcdemo/pb"
	"log"
	"time"

	"google.golang.org/grpc"
)

const (
	address = "localhost:8787"
)

func main() {

	conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	c := pb.NewSaySomethingServiceClient(conn)
	mdfk2020(c, "GG")

}

//
func mdfk2020(c pb.SaySomethingServiceClient, name string) {

	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	res, err := c.MDFK2020(ctx, &pb.MDFKRequest{Name: name})
	if err != nil {
		log.Fatalf("could not mkdfk2020: %v", err)
	}
	log.Printf("gRPC response: %s", res.Data)
}

執行結果

//server
i receive: GG
//client
2020/09/28 13:59:08 gRPC response: Hello GG

gRPC實作上面是比RESTful API麻煩一點,因為多了要先定義好proto,如果要加個欄位變成
1.proto加好欄位
2.進行cli產生新的pb檔
3.server端引用新的pb檔
4.client端引用新的pb檔

視自己工作的需求進行選擇吧~~


上一篇
[DAY19]Gin-Middleware,validator與binding
下一篇
[DAY21]小朋友才要選擇~gRPC與http我全都要之gRPC Gateway
系列文
欸你這週GO了嘛30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言